#!/bin/bash
#################
# Launcher init #
#################

# shellcheck disable=SC2034
START=$(date +%s.%N)

if ! snapctl is-connected "gaming-mesa"; then
  echo "ERROR: not connected to the gaming-mesa content interface."
  echo "To connect:"
  echo "sudo snap connect heroic:gaming-mesa gaming-graphics-core22"
  exit 1
fi

# ensure_dir_exists calls `mkdir -p` if the given path is not a directory.
# This speeds up execution time by avoiding unnecessary calls to mkdir.
#
# Usage: ensure_dir_exists <path> [<mkdir-options>]...
#
function ensure_dir_exists() {
  [ -d "$1" ] ||  mkdir -p "$@"
}

declare -A PIDS
function async_exec() {
  "$@" &
  PIDS[$!]=$*
}
function wait_for_async_execs() {
  for pid in "${!PIDS[@]}"
  do
    wait "$pid" && continue || echo "ERROR: ${PIDS[$pid]} exited abnormally with status $?"
  done
}

# shellcheck source=/dev/null
source "$SNAP_USER_DATA/.last_revision" 2>/dev/null || true
if [ "$SNAP_DESKTOP_LAST_REVISION" = "$SNAP_REVISION" ]; then
  needs_update=false
else
  needs_update=true
fi

# Set $REALHOME to the users real home directory
REALHOME=$(getent passwd $UID | cut -d ':' -f 6)

export SNAP_DESKTOP_RUNTIME=$SNAP

# Set config folder to local path
export XDG_CONFIG_HOME="$SNAP_USER_COMMON/.config"
ensure_dir_exists "$XDG_CONFIG_HOME"
chmod 700 "$XDG_CONFIG_HOME"

# If the user has modified their user-dirs settings, force an update
if [[ -f "$XDG_CONFIG_HOME/user-dirs.dirs.md5sum" ]]; then
  if [[ "$(md5sum < "$REALHOME/.config/user-dirs.dirs")" != "$(cat "$XDG_CONFIG_HOME/user-dirs.dirs.md5sum")" ||
        ( -f "$XDG_CONFIG_HOME/user-dirs.locale.md5sum" &&
          "$(md5sum < "$REALHOME/.config/user-dirs.locale")" != "$(cat "$XDG_CONFIG_HOME/user-dirs.locale.md5sum")" ) ]]; then
    needs_update=true
  fi
else
  # shellcheck disable=SC2034
  needs_update=true
fi

# If the user has saves in the old $SNAP_USER_DATA/.config, move them
# to $SNAP_USER_COMMON/.config
if [[ -d $SNAP_USER_DATA/.config ]]; then
  mv -n $SNAP_USER_DATA/.config/* $XDG_CONFIG_HOME
  rm -r $SNAP_USER_DATA/.config
fi

if [ "$SNAP_ARCH" = "amd64" ]; then
  ARCH="x86_64-linux-gnu"
elif [ "$SNAP_ARCH" = "armhf" ]; then
  ARCH="arm-linux-gnueabihf"
elif [ "$SNAP_ARCH" = "arm64" ]; then
  ARCH="aarch64-linux-gnu"
elif [ "$SNAP_ARCH" = "ppc64el" ]; then
  ARCH="powerpc64le-linux-gnu"
else
  ARCH="$SNAP_ARCH-linux-gnu"
fi

# Force i386
export ARCH="i386-linux-gnu"
export SNAP_LAUNCHER_ARCH_TRIPLET="$ARCH"

###############################################
# Launcher common exports for any desktop app #
###############################################

# Note: We avoid using `eval` because we don't want to expand variable names
#       in paths. For example: LD_LIBRARY_PATH paths might contain `$LIB`.
function prepend_dir() {
  local -n var="$1"
  local dir="$2"
  # We can't check if the dir exists when the dir contains variables
  if [[ "$dir" == *"\$"*  || -d "$dir" ]]; then
    export "${!var}=${dir}${var:+:$var}"
  fi
}

function append_dir() {
  local -n var="$1"
  local dir="$2"
  # We can't check if the dir exists when the dir contains variables
  if [[ "$dir" == *"\$"*  || -d "$dir" ]]; then
    export "${!var}=${var:+$var:}${dir}"
  fi
}


function strip_unreachable_dirs() {
  local -n var=$1
  local dirs="${var}"

  tmp=""
  local IFS=:
  for d in $dirs; do
    # Just checking existence and the `x` bit isn't enough
    # we need to see if we can actually `ls` it because of apparmor
    # quirks
    ls $d > /dev/null 2>&1
    if [[ $? -eq 0 ]]; then
      append_dir tmp "$d"
    else
      echo "INFO: filtering $d out of ${!var} because it is unreachable"
    fi

  done

  export "${!var}=${tmp}"
  unset tmp
}

function is_subpath() {
  dir="$(realpath "$1")"
  parent="$(realpath "$2")"
  [ "${dir##$parent/}" != "$dir" ] && return 0 || return 1
}

append_dir PATH "$SNAP_DESKTOP_RUNTIME/usr/bin"

# XKB config
export XKB_CONFIG_ROOT="$SNAP_DESKTOP_RUNTIME/usr/share/X11/xkb"

# Give XOpenIM a chance to locate locale data.
# This is required for text input to work in SDL2 games.
export XLOCALEDIR="$SNAP_DESKTOP_RUNTIME/usr/share/X11/locale"

# Set XCursors path
export XCURSOR_PATH="$SNAP_DESKTOP_RUNTIME/usr/share/icons"
prepend_dir XCURSOR_PATH "$SNAP/share/icons"

# Mesa Libs for OpenGL support
append_dir LD_LIBRARY_PATH "$SNAP_DESKTOP_RUNTIME/usr/lib/$ARCH/mesa"
append_dir LD_LIBRARY_PATH "$SNAP_DESKTOP_RUNTIME/usr/lib/$ARCH/mesa-egl"
append_dir LD_LIBRARY_PATH "$SNAP_DESKTOP_RUNTIME/usr/lib/x86_64-linux-gnu/mesa"
append_dir LD_LIBRARY_PATH "$SNAP_DESKTOP_RUNTIME/usr/lib/x86_64-linux-gnu/mesa-egl"
# append_dir LD_LIBRARY_PATH "$SNAP_DESKTOP_RUNTIME/graphics/usr/lib/$ARCH/mesa"
# append_dir LD_LIBRARY_PATH "$SNAP_DESKTOP_RUNTIME/graphics/usr/lib/$ARCH/mesa-egl"
# append_dir LD_LIBRARY_PATH "$SNAP_DESKTOP_RUNTIME/graphics/usr/lib/x86_64-linux-gnu/mesa"
# append_dir LD_LIBRARY_PATH "$SNAP_DESKTOP_RUNTIME/graphics/usr/lib/x86_64-linux-gnu/mesa-egl"

append_dir LD_LIBRARY_PATH "$SNAP_DESKTOP_RUNTIME/graphics/usr/lib/$ARCH"
append_dir LD_LIBRARY_PATH "$SNAP_DESKTOP_RUNTIME/graphics/usr/lib/x86_64-linux-gnu"

# Tell libGL and libva where to find the drivers
export LIBGL_DRIVERS_PATH="$SNAP_DESKTOP_RUNTIME/usr/lib/$ARCH/dri"
append_dir LIBGL_DRIVERS_PATH "$SNAP_DESKTOP_RUNTIME/usr/lib/x86_64-linux-gnu/dri"
append_dir LIBGL_DRIVERS_PATH "$SNAP_DESKTOP_RUNTIME/graphics/usr/lib/$ARCH/dri"
append_dir LIBGL_DRIVERS_PATH "$SNAP_DESKTOP_RUNTIME/graphics/usr/lib/x86_64-linux-gnu/dri"
append_dir LIBGL_DRIVERS_PATH "$SNAP_DESKTOP_RUNTIME/graphics/usr/lib/i386-linux-gnu/dri"
append_dir LD_LIBRARY_PATH "$LIBGL_DRIVERS_PATH"

append_dir LIBVA_DRIVERS_PATH "$SNAP_DESKTOP_RUNTIME/usr/lib/$ARCH/dri"
append_dir LIBVA_DRIVERS_PATH "$SNAP_DESKTOP_RUNTIME/usr/lib/x86_64-linux-gnu/dri"
append_dir LIBVA_DRIVERS_PATH "$SNAP_DESKTOP_RUNTIME/graphics/usr/lib/$ARCH/dri"
append_dir LIBVA_DRIVERS_PATH "$SNAP_DESKTOP_RUNTIME/graphics/usr/lib/x86_64-linux-gnu/dri"

# Set where the VDPAU drivers are located
export VDPAU_DRIVER_PATH="/usr/lib/$ARCH/vdpau/"
if [ -e "/var/lib/snapd/lib/gl/vdpau/libvdpau_nvidia.so" ]; then
  export VDPAU_DRIVER_PATH="/var/lib/snapd/lib/gl/vdpau"
  # Prevent picking VA-API (Intel/AMD) over NVIDIA VDPAU; on PRIME systems for example
  unset LIBVA_DRIVERS_PATH
fi

# Export Vulkan ICD filename paths
export VK_ICD_FILENAMES="/var/lib/snapd/lib/vulkan/icd.d/nvidia_icd.json:$SNAP/graphics/usr/share/vulkan/icd.d/radeon_icd.x86_64.json:$SNAP/graphics/usr/share/vulkan/icd.d/radeon_icd.i686.json:$SNAP/graphics/usr/share/vulkan/icd.d/intel_icd.x86_64.json:$SNAP/graphics/usr/share/vulkan/icd.d/intel_icd.i686.json"

# Workaround in snapd for proprietary nVidia drivers mounts the drivers in
# /var/lib/snapd/lib/gl that needs to be in LD_LIBRARY_PATH
# Without that OpenGL using apps do not work with the nVidia drivers.
# Ref.: https://bugs.launchpad.net/snappy/+bug/1588192
append_dir LD_LIBRARY_PATH "/var/lib/snapd/lib/gl"
append_dir LD_LIBRARY_PATH "/var/lib/snapd/lib/gl/vdpau"

# By sheer luck, this was working in the past, but we need this explicitly
append_dir LD_LIBRARY_PATH "/var/lib/snapd/lib/gl32"
append_dir LD_LIBRARY_PATH "/var/lib/snapd/lib/gl32/vdpau"

# Unity7 export (workaround for https://launchpad.net/bugs/1638405)
append_dir LD_LIBRARY_PATH "$SNAP_DESKTOP_RUNTIME/usr/lib/$ARCH/libunity"

# Pulseaudio export
append_dir LD_LIBRARY_PATH "$SNAP_DESKTOP_RUNTIME/usr/lib/$ARCH/pulseaudio"

# EGL vendor files on glvnd enabled systems
prepend_dir __EGL_VENDOR_LIBRARY_DIRS "/var/lib/snapd/lib/glvnd/egl_vendor.d"
append_dir __EGL_VENDOR_LIBRARY_DIRS "$SNAP_DESKTOP_RUNTIME/usr/share/glvnd/egl_vendor.d"

# Tell GStreamer where to find its plugins
export GST_PLUGIN_PATH="$SNAP/usr/lib/$ARCH/gstreamer-1.0"
export GST_PLUGIN_SYSTEM_PATH="$SNAP_DESKTOP_RUNTIME/usr/lib/$ARCH/gstreamer-1.0"
# gst plugin scanner doesn't install in the correct path: https://github.com/ubuntu/snapcraft-desktop-helpers/issues/43
export GST_PLUGIN_SCANNER="$SNAP_DESKTOP_RUNTIME/usr/lib/$ARCH/gstreamer1.0/gstreamer-1.0/gst-plugin-scanner"

# XDG Config
prepend_dir XDG_CONFIG_DIRS "$SNAP_DESKTOP_RUNTIME/etc/xdg"
prepend_dir XDG_CONFIG_DIRS "$SNAP/etc/xdg"

# Define snaps' own data dir
prepend_dir XDG_DATA_DIRS "$SNAP_DESKTOP_RUNTIME/usr/share"
prepend_dir XDG_DATA_DIRS "$SNAP/usr/share"
prepend_dir XDG_DATA_DIRS "$SNAP/share"
prepend_dir XDG_DATA_DIRS "$SNAP/data-dir"
prepend_dir XDG_DATA_DIRS "$SNAP_USER_DATA"
append_dir XDG_DATA_DIRS "$SNAP/graphics/usr/share"

# Set XDG_DATA_HOME to local path
# heroic snap
export XDG_DATA_HOME="$SNAP_USER_COMMON/.local/share"
ensure_dir_exists "$XDG_DATA_HOME"

# Workaround for GLib < 2.53.2 not searching for schemas in $XDG_DATA_HOME:
#   https://bugzilla.gnome.org/show_bug.cgi?id=741335
prepend_dir XDG_DATA_DIRS "$XDG_DATA_HOME"

# Set cache folder to local path
export XDG_CACHE_HOME="$SNAP_USER_COMMON/.cache"
if [[ -d "$SNAP_USER_DATA/.cache" && ! -e "$XDG_CACHE_HOME" ]]; then
  # the .cache directory used to be stored under $SNAP_USER_DATA, migrate it
  mv "$SNAP_USER_DATA/.cache" "$SNAP_USER_COMMON/"
fi
ensure_dir_exists "$XDG_CACHE_HOME"

# Create $XDG_RUNTIME_DIR if not exists (to be removed when LP: #1656340 is fixed)
# shellcheck disable=SC2174
ensure_dir_exists "$XDG_RUNTIME_DIR" -m 700

# Ensure the app finds locale definitions (requires locales-all to be installed)
append_dir LOCPATH "$SNAP_DESKTOP_RUNTIME/usr/lib/locale"

# If detect wayland server socket, then set environment so applications prefer
# wayland, and setup compat symlink (until we use user mounts. Remember,
# XDG_RUNTIME_DIR is /run/user/<uid>/snap.$SNAP so look in the parent directory
# for the socket. For details:
# https://forum.snapcraft.io/t/wayland-dconf-and-xdg-runtime-dir/186/10
# Applications that don't support wayland natively may define DISABLE_WAYLAND
# (to any non-empty value) to skip that logic entirely.
wayland_available=false
if [[ -n "$XDG_RUNTIME_DIR" && -z "$DISABLE_WAYLAND" ]]; then
    wdisplay="wayland-0"
    if [ -n "$WAYLAND_DISPLAY" ]; then
        wdisplay="$WAYLAND_DISPLAY"
    fi
    wayland_sockpath="$XDG_RUNTIME_DIR/../$wdisplay"
    wayland_snappath="$XDG_RUNTIME_DIR/$wdisplay"
    if [ -S "$wayland_sockpath" ]; then
        # if running under wayland, use it
        #export WAYLAND_DEBUG=1
        # shellcheck disable=SC2034
        wayland_available=true
        # create the compat symlink for now
        if [ ! -e "$wayland_snappath" ]; then
            ln -s "$wayland_sockpath" "$wayland_snappath"
        fi
    fi
fi

# Make PulseAudio socket available inside the snap-specific $XDG_RUNTIME_DIR
if [ -n "$XDG_RUNTIME_DIR" ]; then
    pulsenative="pulse/native"
    pulseaudio_sockpath="$XDG_RUNTIME_DIR/../$pulsenative"
    if [ -S "$pulseaudio_sockpath" ]; then
        export PULSE_SERVER="unix:${pulseaudio_sockpath}"
    fi
fi

# Keep an array of data dirs, for looping through them
IFS=':' read -r -a data_dirs_array <<< "$XDG_DATA_DIRS"

if [ "$needs_update" = true ]; then
  rm -rf "$XDG_DATA_HOME"/{themes,.themes}
fi

##############################
# GTK launcher specific part #
##############################

# shellcheck disable=SC2154
if [ "$wayland_available" = true ]; then
  export GDK_BACKEND="wayland"
  export CLUTTER_BACKEND="wayland"
  # Does not hurt to specify this as well, just in case
  export QT_QPA_PLATFORM=wayland-egl
fi
append_dir GTK_PATH "$SNAP_DESKTOP_RUNTIME/usr/lib/$ARCH/gtk-3.0"

###############################
# Mark update and exec binary #
###############################

# shellcheck disable=SC2154
[ "$needs_update" = true ] && echo "SNAP_DESKTOP_LAST_REVISION=$SNAP_REVISION" > "$SNAP_USER_DATA/.last_revision"

wait_for_async_execs

if [ -n "$SNAP_DESKTOP_DEBUG" ]; then
  echo "desktop-launch elapsed time: $(date +%s.%N --date="$START seconds ago")"
  echo "Now running: exec $*"
fi

# Fix default run command
# The default works on a system with only the Snap installed, but if they also
# have the deb then the default command will run the deb instead of the Snap.
find "$SNAP_USER_COMMON/.local/share/applications" -type f | while read -r file; do
  sed -i "s/Exec=heroic/Exec=snap run heroic/" "$file"
done

# Links game icons to host
find "$SNAP_USER_COMMON/.local/share/icons/hicolor" -type f -name "heroic_icon_*.png" | while read -r file; do
  dest="${file/$SNAP_USER_COMMON/$REALHOME}"
  ensure_dir_exists "$(dirname $dest)"
  ln -sf "$file" "$dest"
done

# Link .desktop files to host
ln -sf $SNAP_USER_COMMON/.local/share/applications/* $REALHOME/.local/share/applications/

strip_unreachable_dirs XDG_DATA_DIRS
strip_unreachable_dirs XDG_CONFIG_DIRS
strip_unreachable_dirs XDG_SPECIAL_DIRS

# Even with libfuse, appimages need to be extracted to run.
# This is a workaround until we figure out something better, because it makes
# launching AppImage games way slower
#
# That said, they're really rare so it's not a big deal
export APPIMAGE_EXTRACT_AND_RUN=1

$SNAP/bin/nvidia32 &
"$@"
